/**
 * Project Name:SimonFramework
 * File Name:DefaultConfigurationFactory.java
 * Package Name:com.simon.framework.image.core
 * Date:2016-5-19 下午3:46:45
 * Copyright (c) 2016, simon@cmonbaby.com All Rights Reserved.
 */
package com.cmonbaby.images.core;

import android.annotation.TargetApi;
import android.app.ActivityManager;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.os.Build;

import com.cmonbaby.images.cache.disc.DiskCache;
import com.cmonbaby.images.cache.disc.impl.UnlimitedDiskCache;
import com.cmonbaby.images.cache.disc.impl.ext.LruDiskCache;
import com.cmonbaby.images.cache.disc.naming.FileNameGenerator;
import com.cmonbaby.images.cache.disc.naming.HashCodeFileNameGenerator;
import com.cmonbaby.images.cache.memory.MemoryCache;
import com.cmonbaby.images.cache.memory.impl.LruMemoryCache;
import com.cmonbaby.images.core.assist.QueueProcessingType;
import com.cmonbaby.images.core.assist.deque.LIFOLinkedBlockingDeque;
import com.cmonbaby.images.core.decode.BaseImageDecoder;
import com.cmonbaby.images.core.decode.ImageDecoder;
import com.cmonbaby.images.core.display.BitmapDisplayer;
import com.cmonbaby.images.core.display.SimpleBitmapDisplayer;
import com.cmonbaby.images.core.download.BaseImageDownloader;
import com.cmonbaby.images.core.download.ImageDownloader;
import com.cmonbaby.images.utils.Cons;
import com.cmonbaby.images.utils.LogUtils;
import com.cmonbaby.images.utils.StorageUtils;

import java.io.File;
import java.io.IOException;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * <p>Author:      Simon
 * <p>QO:          8950764
 * <p>Email:       simon@cmonbaby.com
 * <p>WebSize:     https://www.cmonbaby.com
 * <p>Version:     1.0.0
 * <p>Date:        2020/12/29
 * <p>Description: 工厂提供的默认选项ImageLoaderConfiguration配置
 */
public class DefaultConfigurationFactory {

    /** 创建默认的实现任务执行 */
    public static Executor createExecutor(int threadPoolSize, int threadPriority,
                                          QueueProcessingType tasksProcessingType) {
        boolean lifo = tasksProcessingType == QueueProcessingType.LIFO;
        BlockingQueue<Runnable> taskQueue =
                lifo ? new LIFOLinkedBlockingDeque<Runnable>() : new LinkedBlockingQueue<Runnable>();
        return new ThreadPoolExecutor(threadPoolSize, threadPoolSize, 0L, TimeUnit.MILLISECONDS, taskQueue,
                createThreadFactory(threadPriority, "uil-pool-"));
    }

    /** 创建默认的实现任务分配器 */
    public static Executor createTaskDistributor() {
        return Executors.newCachedThreadPool(createThreadFactory(Thread.NORM_PRIORITY, "uil-pool-d-"));
    }

    /** 创建 {@linkplain HashCodeFileNameGenerator 默认实现} of FileNameGenerator */
    public static FileNameGenerator createFileNameGenerator() {
        return new HashCodeFileNameGenerator();
    }

    /**
     * 创建默认的实现 {@link DiskCache} 取决于传入参数
     */
    public static DiskCache createDiskCache(Context context, FileNameGenerator diskCacheFileNameGenerator,
                                            long diskCacheSize, int diskCacheFileCount) {
        File reserveCacheDir = createReserveDiskCacheDir(context);
        if (diskCacheSize > 0 || diskCacheFileCount > 0) {
            File individualCacheDir = StorageUtils.getIndividualCacheDirectory(context);
            try {
                return new LruDiskCache(individualCacheDir, reserveCacheDir, diskCacheFileNameGenerator, diskCacheSize,
                        diskCacheFileCount);
            } catch (IOException e) {
                LogUtils.e(Cons.LOG_TAG, e);
                // continue and create unlimited cache
            }
        }
        File cacheDir = StorageUtils.getCacheDirectory(context);
        return new UnlimitedDiskCache(cacheDir, reserveCacheDir, diskCacheFileNameGenerator);
    }

    /**
     * 创建默认的实现 {@link DiskCache} 取决于传入参数
     */
    public static DiskCache createDiskCache(Context context, File cacheDir, FileNameGenerator diskCacheFileNameGenerator,
                                            long diskCacheSize, int diskCacheFileCount) {
        File reserveCacheDir = createReserveDiskCacheDir(context);
        if (diskCacheSize > 0 || diskCacheFileCount > 0) {
            try {
                return new LruDiskCache(cacheDir, reserveCacheDir, diskCacheFileNameGenerator, diskCacheSize,
                        diskCacheFileCount);
            } catch (IOException e) {
                LogUtils.e(Cons.LOG_TAG, e);
            }
        }
        return new UnlimitedDiskCache(cacheDir, reserveCacheDir, diskCacheFileNameGenerator);
    }

    /** 如果主磁盘缓存文件夹不可用，则创建储备将使用磁盘缓存文件夹 */
    public static File createReserveDiskCacheDir(Context context) {
        File cacheDir = StorageUtils.getCacheDirectory(context, false);
        File individualDir = new File(cacheDir, "uil-images");
        if (individualDir.exists() || individualDir.mkdir()) {
            cacheDir = individualDir;
        }
        return cacheDir;
    }

    /**
     * 创建默认实现 {@link MemoryCache} - {@link LruMemoryCache}<br />
     * 默认的缓存大小=可用应用程序内存的1/8
     */
    public static MemoryCache createMemoryCache(Context context, int memoryCacheSize) {
        if (memoryCacheSize == 0) {
            ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
            int memoryClass = am.getMemoryClass();
            if (hasHoneycomb() && isLargeHeap(context)) {
                memoryClass = getLargeMemoryClass(am);
            }
            memoryCacheSize = 1024 * 1024 * memoryClass / 8;
        }
        return new LruMemoryCache(memoryCacheSize);
    }

    private static boolean hasHoneycomb() {
        return Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB;
    }

    @TargetApi(Build.VERSION_CODES.HONEYCOMB)
    private static boolean isLargeHeap(Context context) {
        return (context.getApplicationInfo().flags & ApplicationInfo.FLAG_LARGE_HEAP) != 0;
    }

    @TargetApi(Build.VERSION_CODES.HONEYCOMB)
    private static int getLargeMemoryClass(ActivityManager am) {
        return am.getLargeMemoryClass();
    }

    /** 创建默认实现 {@link ImageDownloader} - {@link BaseImageDownloader} */
    public static ImageDownloader createImageDownloader(Context context) {
        return new BaseImageDownloader(context);
    }

    /** 创建默认的实现 {@link ImageDecoder} - {@link BaseImageDecoder} */
    public static ImageDecoder createImageDecoder(boolean loggingEnabled) {
        return new BaseImageDecoder(loggingEnabled);
    }

    /** 创建默认的实现 {@link BitmapDisplayer} - {@link SimpleBitmapDisplayer} */
    public static BitmapDisplayer createBitmapDisplayer() {
        return new SimpleBitmapDisplayer();
    }

    /** 创建默认实现的 {@linkplain ThreadFactory thread factory} 任务执行 */
    private static ThreadFactory createThreadFactory(int threadPriority, String threadNamePrefix) {
        return new DefaultThreadFactory(threadPriority, threadNamePrefix);
    }

    private static class DefaultThreadFactory implements ThreadFactory {

        private static final AtomicInteger poolNumber = new AtomicInteger(1);

        private final ThreadGroup group;
        private final AtomicInteger threadNumber = new AtomicInteger(1);
        private final String namePrefix;
        private final int threadPriority;

        DefaultThreadFactory(int threadPriority, String threadNamePrefix) {
            this.threadPriority = threadPriority;
            group = Thread.currentThread().getThreadGroup();
            namePrefix = threadNamePrefix + poolNumber.getAndIncrement() + "-thread-";
        }

        @Override
        public Thread newThread(Runnable r) {
            Thread t = new Thread(group, r, namePrefix + threadNumber.getAndIncrement(), 0);
            if (t.isDaemon()) t.setDaemon(false);
            t.setPriority(threadPriority);
            return t;
        }
    }

}

